package utils

import java.io.{File, FileInputStream}
import java.lang.reflect.Field

import javax.imageio.ImageIO
import org.apache.commons.io.{FileUtils, IOUtils}
import org.apache.commons.lang3.StringUtils
//import org.apache.commons.math3.stat.StatUtils
//import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation
//import org.saddle.io._
//import CsvImplicits._
//import javax.imageio.ImageIO
import org.apache.commons.codec.binary.Base64
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.rendering.PDFRenderer
import play.api.libs.json.Json

import scala.collection.JavaConverters._
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import org.joda.time.DateTime
//import scala.math.log10
import implicits.Implicits._

object Utils {

  val scriptHtml =
    """
      |<script>
      |	$(function () {
      |			    $("footer:first").remove()
      |        $("#content").css("margin","0")
      |       $(".linkheader>a").each(function () {
      |				   var text=$(this).text()
      |				   $(this).replaceWith("<span style='color: #222222;'>"+text+"</span>")
      |			   })
      |
      |      $("tr").each(function () {
      |         var a=$(this).find("td>a:last")
      |					var text=a.text()
      |					a.replaceWith("<span style='color: #222222;'>"+text+"</span>")
      |				})
      |
      |       $("p.titleinfo>a").each(function () {
      |				   var text=$(this).text()
      |				   $(this).replaceWith("<span style='color: #606060;'>"+text+"</span>")
      |			   })
      |
      |       $(".param:eq(1)").parent().hide()
      |       $(".linkheader").hide()
      |
      |			})
      |</script>
    """.stripMargin

  val goPy = {
    val path = "C:\\Python\\python.exe"
    if (new File(path).exists()) path else "python"
  }

  val pyScript =
    """
      |<script>
      |Plotly.Plots.resize(document.getElementById($('#charts').children().eq(0).attr("id")));
      |window.addEventListener("resize", function (ev) {
      |				Plotly.Plots.resize(document.getElementById($('#charts').children().eq(0).attr("id")));
      |					})
      |</script>
      |
    """.stripMargin

  def createDirectoryWhenNoExist(file: File): Unit = {
    if (!file.exists && !file.isDirectory) file.mkdir
  }

  def deleteDirectory(direcotry: File) = {
    try {
      FileUtils.deleteDirectory(direcotry)
    } catch {
      case _ =>
    }
  }

  def getTime(startTime: Long) = {
    val endTime = System.currentTimeMillis()
    (endTime - startTime) / 1000.0
  }

  def isDoubleP(value: String, p: Double => Boolean): Boolean = {
    try {
      val dbValue = value.toDouble
      p(dbValue)
    } catch {
      case _: Exception =>
        false
    }
  }

  def getGroupNum(content: String) = {
    content.split(";").size
  }

  def productGroupFile(tmpDir: File, content: String) = {
    val groupFile = new File(tmpDir, "group.txt")
    val groupLines = List("Sample", "Group")
    val newLines = content.mySplit(";").flatMap { group =>
      val groupName = group.split(":")(0)
      val sampleNames = group.split(":")(1).mySplit(",")
      sampleNames.map { sampleName =>
        List(sampleName, groupName)
      }
    }
    (groupLines :: newLines).toTxtFile(groupFile)
  }

  def getMap(content: String) = {
    val map = mutable.LinkedHashMap[String, mutable.Buffer[String]]()
    content.split(";").foreach { x =>
      val columns = x.split(":")
      map += (columns(0) -> columns(1).split(",").toBuffer)
    }
    map
  }

  def getGroupNames(content: String) = {
    val map = getMap(content)
    map.keys.toBuffer

  }

  def replaceByRate(dataFile: File, rate: Double) = {
    val buffer = FileUtils.readLines(dataFile).asScala
    val array = buffer.map(_.split("\t"))
    val minValue = array.drop(1).flatMap(_.tail).filter(Utils.isDoubleP(_, _ > 0)).map(_.toDouble).min
    val rateMinValue = minValue * rate
    for (i <- 1 until array.length; j <- 1 until array(i).length) {
      if (Utils.isDoubleP(array(i)(j), _ == 0)) array(i)(j) = rateMinValue.toString
      if (StringUtils.isBlank(array(i)(j))) array(i)(j) = rateMinValue.toString
    }
    FileUtils.writeLines(dataFile, array.map(_.mkString("\t")).asJava)
  }

  def replaceByValue(dataFile: File, assignValue: String) = {
    val buffer = FileUtils.readLines(dataFile).asScala
    val array = buffer.map(_.split("\t"))
    for (i <- 1 until array.length; j <- 1 until array(i).length) {
      if (Utils.isDoubleP(array(i)(j), _ == 0)) array(i)(j) = assignValue
      if (StringUtils.isBlank(array(i)(j))) array(i)(j) = assignValue
    }
    FileUtils.writeLines(dataFile, array.map(_.mkString("\t")).asJava)
  }

  def relace0byNan(dataFile: File) = {
    val buffer = FileUtils.readLines(dataFile).asScala
    val array = buffer.map(_.split("\t"))
    for (i <- 1 until array.length; j <- 1 until array(i).length) {
      if (Utils.isDoubleP(array(i)(j), _ == 0)) array(i)(j) = "NA"
    }
    FileUtils.writeLines(dataFile, array.map(_.mkString("\t")).asJava)
  }

  def innerNormal(dataFile: File, colName: String, rowName: String) = {
    val buffer = FileUtils.readLines(dataFile).asScala
    val array = buffer.map(_.split("\t"))
    val colNum = array.take(1).flatten.indexOf(colName)
    val rowNum = array.map(_ (0)).indexOf(rowName)
    val div = array(rowNum)(colNum).toDouble
    val divArray = array(rowNum).drop(1).map(div / _.toDouble)
    for (i <- 1 until array.length; j <- 1 until array(i).length) {
      array(i)(j) = (array(i)(j).toDouble * divArray(j - 1)).toString
    }
    FileUtils.writeLines(dataFile, array.map(_.mkString("\t")).asJava)
  }

  def qcNormal(dataFile: File, colName: String) = {
    val buffer = FileUtils.readLines(dataFile).asScala
    val array = buffer.map(_.split("\t"))
    val colNum = array.head.indexOf(colName)
    for (i <- 1 until array.length) {
      val div = array(i)(colNum).toDouble
      for (j <- 1 until array(i).length) {
        array(i)(j) = (array(i)(j).toDouble / div).toString
      }
    }
    FileUtils.writeLines(dataFile, array.map(_.mkString("\t")).asJava)
  }

  def lfJoin(seq: Seq[String]) = {
    seq.mkString("\n")
  }

  def execFuture[T](f: Future[T]): T = {
    Await.result(f, Duration.Inf)
  }

  def getArrayByTs[T](x: Seq[T]) = {
    x.map { y =>
      y.getClass.getDeclaredFields.toBuffer.map { x: Field =>
        x.setAccessible(true)
        val kind = x.get(y)
        val value = kind match {
          case x if x.isInstanceOf[DateTime] => val time = x.asInstanceOf[DateTime]
            time.toString("yyyy-MM-dd HH:mm:ss")
          case _ => kind.toString
        }
        (x.getName, value)
      }.init.toMap
    }
  }

  def peakAreaNormal(dataFile: File, coefficient: Double) = {
    val buffer = FileUtils.readLines(dataFile).asScala
    val array = buffer.map(_.split("\t"))
    val sumArray = new Array[Double](array(0).length)
    for (i <- 1 until array.length; j <- 1 until array(i).length) {
      sumArray(j) += array(i)(j).toDouble
    }
    for (i <- 1 until array.length; j <- 1 until array(i).length) {
      array(i)(j) = (coefficient * array(i)(j).toDouble / sumArray(j)).toString
    }
    FileUtils.writeLines(dataFile, array.map(_.mkString("\t")).asJava)
  }

  //
  //  def log2(x: Double) = log10(x) / log10(2.0)
  //
  //  def getStdErr(values: Array[Double]) = {
  //    val standardDeviation = new StandardDeviation
  //    val stderr = standardDeviation.evaluate(values) / Math.sqrt(values.length)
  //    stderr
  //  }

  def dealGeneIds(geneId: String) = {
    geneId.split("\n").map(_.trim).distinct.toBuffer
  }

  def getDataJson(file: File) = {
    val lines = FileUtils.readLines(file).asScala
    val sampleNames = lines.head.split("\t").drop(1)
    val array = lines.drop(1).map { line =>
      val columns = line.split("\t")
      val map = mutable.Map[String, String]()
      map += ("geneId" -> columns(0))
      sampleNames.zip(columns.drop(1)).foreach { case (sampleName, data) =>
        map += (sampleName -> data)
      }
      map
    }
    Json.obj("array" -> array, "sampleNames" -> sampleNames)
  }

    def pdf2png(tmpDir: File, fileName: String) = {
      val pdfFile = new File(tmpDir, fileName)
      val outFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".png"
      val outFile = new File(tmpDir, outFileName)
      val document = PDDocument.load(pdfFile)
      val renderer = new PDFRenderer(document)
      ImageIO.write(renderer.renderImage(0, 3), "png", outFile)
      document.close()
    }

  def getInfoByFile(file: File) = {
    val lines = FileUtils.readLines(file).asScala
    val columnNames = lines.head.split("\t").drop(1)
    val array = lines.drop(1).map { line =>
      val columns = line.split("\t")
      val map = mutable.Map[String, String]()
      map += ("geneId" -> columns(0))
      columnNames.zip(columns.drop(1)).foreach { case (columnName, data) =>
        map += (columnName -> data)
      }
      map
    }
    (columnNames, array)
  }

  def checkFile(file: File): (Boolean, String) = {
    val buffer = FileUtils.readLines(file).asScala
    val headers = buffer.head.split("\t")
    var error = ""
    if (headers.size < 2) {
      error = "错误：文件列数小于2！"
      return (false, error)
    }
    val headersNoHead = headers.drop(1)
    val repeatElement = headersNoHead.diff(headersNoHead.distinct).distinct.headOption
    repeatElement match {
      case Some(x) => val nums = headers.zipWithIndex.filter(_._1 == x).map(_._2 + 1).mkString("(", "、", ")")
        error = "错误：样品名" + x + "在第" + nums + "列重复出现！"
        return (false, error)
      case None =>
    }

    val ids = buffer.drop(1).map(_.split("\t")(0))
    val repeatid = ids.diff(ids.distinct).distinct.headOption
    repeatid match {
      case Some(x) => val nums = ids.zipWithIndex.filter(_._1 == x).map(_._2 + 2).mkString("(", "、", ")")
        error = "错误：第一列:" + x + "在第" + nums + "行重复出现！"
        return (false, error)
      case None =>
    }

    val headerSize = headers.size
    for (i <- 1 until buffer.size) {
      val columns = buffer(i).split("\t")
      if (columns.size != headerSize) {
        error = "错误：数据文件第" + (i + 1) + "行列数不对！"
        return (false, error)
      }

    }

    for (i <- 1 until buffer.size) {
      val columns = buffer(i).split("\t")
      for (j <- 1 until columns.size) {
        val value = columns(j)
        if (!isDouble(value)) {
          error = "错误：数据文件第" + (i + 1) + "行第" + (j + 1) + "列不为数字！"
          return (false, error)
        }
      }
    }
    (true, error)
  }

  def isDouble(value: String): Boolean = {
    try {
      value.toDouble
    } catch {
      case _: Exception =>
        return false
    }
    true
  }

    def getBase64Str(imageFile: File): String = {
      val inputStream = new FileInputStream(imageFile)
      val bytes = IOUtils.toByteArray(inputStream)
      val bytes64 = Base64.encodeBase64(bytes)
      inputStream.close()
      new String(bytes64)
    }

}
